﻿using Ionic.Zip;
using Jerry.DroidBuilder.Common;
using Jerry.DroidBuilder.Properties;

namespace Jerry.DroidBuilder.Helpers
{
    public static class PackageHelper
    {
        #region Packaging with ZipLib

        public static void Create(string directory, string fileName)
        {
            using (var zip = new ZipFile())
            {
                zip.AddDirectory(directory, string.Empty);
                zip.SaveProgress += OnSaveProgress;
                zip.Save(fileName);
            }
        }

        public static void UpdateFile(string fileName, string fileToCompress)
        {
            using (var zip = ZipFile.Read(fileName))
            {
                zip.UpdateFile(fileToCompress, string.Empty);
                zip.Save(fileName);
            }
        }

        public static void UpdateDirectory(string fileName, string directoryToCompress)
        {
            using (var zip = ZipFile.Read(fileName))
            {
                zip.UpdateDirectory(directoryToCompress, string.Empty);
                zip.Save(fileName);
            }
        }

        public static bool Contains(string fileName, string name)
        {
            using (var zip = ZipFile.Read(fileName))
            {
                var result = zip.ContainsEntry(name);

                if (!result && name.EndsWith("/"))
                {
                    foreach (var key in zip.EntryFileNames)
                    {
                        if (key.StartsWith(name))
                        {
                            result = true;
                            break;
                        }
                    }
                }

                return result;
            }
        }

        public static void Extract(string fileName, string targetDirectory)
        {
            using (var zip = ZipFile.Read(fileName))
            {
                zip.ExtractAll(targetDirectory, ExtractExistingFileAction.OverwriteSilently);
            }
        }

        public static void Delete(string fileName, string name)
        {
            using (var zip = ZipFile.Read(fileName))
            {
                zip.RemoveEntry(name);
                zip.Save(fileName);
            }
        }

        private static void OnSaveProgress(object sender, SaveProgressEventArgs e)
        {
            if (e.EventType == ZipProgressEventType.Saving_AfterWriteEntry)
            {
                var progress = (double)e.EntriesSaved / (double)e.EntriesTotal;

                StatusReport.Update(Lang.Build_Compress_Progress, progress.ToString("P0"));
            }
        }

        #endregion

        #region Packaging with Shell

        //private const int SHCONTF_NONFOLDERS = 64;

        //public static void CreateZipPackage(string directory, string fileName)
        //{
        //    var zipHeader = new byte[] { 80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };

        //    using (var fs = File.Create(fileName))
        //    {
        //        fs.Write(zipHeader, 0, zipHeader.Length);
        //    }

        //    var shell = new ShellClass();
        //    var sourceFolder = shell.NameSpace(directory);
        //    var targetFolder = shell.NameSpace(fileName);
        //    var sourceItems = sourceFolder.Items();
        //    var originalCount = CountItems(sourceItems, true);
        //    var deadline = DateTime.Now.AddMinutes(30);

        //    targetFolder.CopyHere(sourceItems, 4 | 16 | 1024);

        //    while (true)
        //    {
        //        if (DateTime.Now >= deadline)
        //        {
        //            break;
        //        }

        //        var zippedCount = CountItems(targetFolder.Items(), true);

        //        if (originalCount == zippedCount)
        //        {
        //            break;
        //        }

        //        Thread.Sleep(2500);
        //    }
        //}

        //public static bool Contains(string fileName, string name)
        //{
        //    var shell = new ShellClass();
        //    var sourceFolder = shell.NameSpace(fileName);
        //    var sourceItems = sourceFolder.Items();

        //    for (int i = 0; i < sourceItems.Count; i++)
        //    {
        //        var item = sourceItems.Item(i);

        //        if (item.Name.Equals(name))
        //        {
        //            return true;
        //        }
        //    }

        //    return false;
        //}

        //public static void Delete(string fileName, string deleteName)
        //{
        //    var shell = new ShellClass();
        //    var targetFolder = shell.NameSpace(fileName);

        //    var hierarchy = GetHierarchy(deleteName);

        //    DeleteFromPackage(shell, targetFolder, hierarchy);
        //}

        //public static void Extract(string fileName, string directory)
        //{
        //    var shell = new ShellClass();
        //    var sourceFolder = shell.NameSpace(fileName);
        //    var targetFolder = shell.NameSpace(directory);
        //    var sourceItems = sourceFolder.Items();
        //    var length = 5000L;

        //    if (File.Exists(fileName))
        //    {
        //        length = new FileInfo(fileName).Length;
        //    }
        //    else if (Directory.Exists(fileName))
        //    {
        //        length = Directory.GetFiles(fileName, "*", SearchOption.AllDirectories).Length * 1000000;
        //    }

        //    var deadline = DateTime.Now.AddMilliseconds(length / 1000 + 2000);

        //    targetFolder.CopyHere(sourceItems, 4 | 16 | 1024);

        //    while (true)
        //    {
        //        if (DateTime.Now >= deadline)
        //        {
        //            break;
        //        }

        //        //var extractedCount = CountItems(targetFolder.Items(), true);

        //        //if (originalCount == extractedCount)
        //        //{
        //        //    break;
        //        //}

        //        Thread.Sleep(2500);
        //    }
        //}

        //public static void Update(string source, string target, bool sourceIsPackage)
        //{
        //    var tempDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());

        //    Directory.CreateDirectory(tempDirectory);

        //    Extract(source, tempDirectory);
        //    Extract(tempDirectory, target);

        //    Directory.Delete(tempDirectory, true);
        //}

        //public static void UpdateZipPackage(string fileName, string fileToBeCompressed)
        //{
        //    UpdateZipPackage(fileName, fileToBeCompressed, Path.GetFileName(fileToBeCompressed));
        //}

        //public static void UpdateZipPackage(string fileName, string fileToBeCompressed, string targetPath)
        //{
        //    var shell = new ShellClass();
        //    var originalFileName = fileName;
        //    var source = Path.GetDirectoryName(fileToBeCompressed);
        //    var name = Path.GetFileName(fileToBeCompressed);
        //    var hierarchy = GetHierarchy(targetPath);

        //    if (!fileName.EndsWith(".zip"))
        //    {
        //        File.Move(fileName, fileName + ".zip");
        //        fileName += ".zip";
        //    }

        //    var sourceFolder = shell.NameSpace(source);
        //    var targetFolder = shell.NameSpace(fileName);
        //    var sourceItems = sourceFolder.Items() as FolderItems3;
        //    var targetItems = targetFolder.Items();

        //    targetFolder = DeleteFromPackage(shell, targetFolder, hierarchy);

        //    var originalCount = CountItems(targetFolder.Items(), hierarchy.Length > 1);
        //    var deadline = DateTime.Now.AddMinutes(30);

        //    sourceItems.Filter(SHCONTF_NONFOLDERS, name);
        //    targetFolder.CopyHere(sourceItems, 4 | 16 | 1024);

        //    while (true)
        //    {
        //        if (DateTime.Now >= deadline)
        //        {
        //            break;
        //        }

        //        int zippedCount = CountItems(targetFolder.Items(), hierarchy.Length > 1);

        //        if (originalCount == zippedCount - 1)
        //        {
        //            break;
        //        }

        //        Thread.Sleep(500);
        //    }

        //    if (fileName != originalFileName)
        //    {
        //        File.Move(fileName, originalFileName);
        //    }
        //}

        //private static string[] GetHierarchy(string targetPath)
        //{
        //    if (string.IsNullOrEmpty(targetPath))
        //    {
        //        targetPath = string.Empty;
        //    }

        //    return Regex.Split(targetPath, "\\\\|/");
        //}

        //private static Folder DeleteFromPackage(ShellClass shell, Folder folder, string[] hierarchy)
        //{
        //    // Check whether the file already exists in the package.
        //    for (int level = 0; level < hierarchy.Length; level++)
        //    {
        //        var name = hierarchy[level];
        //        var items = folder.Items();
        //        var count = items.Count;

        //        for (int i = 0; i < count; i++)
        //        {
        //            var item = items.Item(i);

        //            if (item.IsFolder && item.Name == name)
        //            {
        //                folder = item.GetFolder as Folder;
        //                break;
        //            }
        //            else if (item.Name == name)
        //            {
        //                var tempFolder = Path.GetTempPath();
        //                var tempName = Path.Combine(tempFolder, name);

        //                if (File.Exists(tempName))
        //                {
        //                    File.Delete(tempName);
        //                }

        //                shell.NameSpace(tempFolder).MoveHere(item, 4 | 16 | 1024);

        //                File.Delete(tempName);
        //                break;
        //            }
        //        }
        //    }

        //    return folder;
        //}

        ///// <summary>
        ///// Get the number of files and folders in the source location
        ///// including all subfolders
        ///// </summary>
        //private static int CountItems(FolderItems folderItems, bool recursive)
        //{
        //    int count = 0;

        //    for (int i = 0; i < folderItems.Count; i++)
        //    {
        //        var item = folderItems.Item(i);

        //        if (item.IsFolder && !item.IsBrowsable && recursive)
        //        {
        //            var folder = item.GetFolder as Folder;
        //            count += CountItems(folder.Items(), recursive);
        //        }
        //        else
        //        {
        //            count++;
        //        }
        //    }

        //    return count;
        //}

        #endregion

        #region Packaging with SharpZipLib

        //public static void CreatePackage(string directory, string fileName)
        //{
        //    try
        //    {
        //        var fileNames = Directory.GetFiles(directory, "*", SearchOption.AllDirectories);

        //        using (var fileStream = new FileStream(fileName, FileMode.Create))
        //        {
        //            using (ZipOutputStream stream = new ZipOutputStream(fileStream))
        //            {
        //                stream.SetLevel(9);
        //                stream.UseZip64 = UseZip64.Off;

        //                foreach (string file in fileNames)
        //                {
        //                    if (file.EndsWith("META-INF\\CERT.RSA", StringComparison.OrdinalIgnoreCase)
        //                        || file.EndsWith("META-INF\\CERT.SF", StringComparison.OrdinalIgnoreCase)
        //                        || file.EndsWith("META-INF\\MANIFEST.MF", StringComparison.OrdinalIgnoreCase))
        //                    {
        //                        continue;
        //                    }

        //                    var buffer = File.ReadAllBytes(file);

        //                    var entry = new ZipEntry(file.Remove(0, directory.Length + 1))
        //                    {
        //                        Size = buffer.Length,
        //                        DateTime = File.GetLastWriteTime(file),
        //                    };

        //                    stream.PutNextEntry(entry);
        //                    stream.Write(buffer, 0, buffer.Length);
        //                }
        //            }
        //        }
        //    }
        //    catch (Exception ex)
        //    {
        //        Console.WriteLine("Exception during processing {0}", ex);
        //    }
        //}

        //public static void AddToPackage(string fileName, string fileTobeCompress)
        //{
        //    var buffer = new byte[4096];
        //    var memoryStream = new MemoryStream();

        //    using (var stream = File.OpenRead(fileName))
        //    {
        //        StreamUtils.Copy(stream, memoryStream, buffer);
        //    }

        //    using (ZipFile zipFile = new ZipFile(memoryStream) { IsStreamOwner = false })
        //    {
        //        zipFile.BeginUpdate();
        //        zipFile.Add(fileTobeCompress, Path.GetFileName(fileTobeCompress));
        //        zipFile.CommitUpdate();
        //    }

        //    memoryStream.Position = 0;

        //    using (var stream = File.Create(fileName))
        //    {
        //        StreamUtils.Copy(memoryStream, stream, buffer);
        //    }
        //} 

        #endregion

        #region Packaging with ZipPackage

        //public static void CreatePackage(string directory, string packageName)
        //{
        //    using (var dummPackage = Package.Open(packageName + ".dummy", FileMode.Create))
        //    {
        //        var field = typeof(ZipPackage).GetField("_contentTypeHelper", BindingFlags.Instance | BindingFlags.NonPublic);

        //        using (var package = Package.Open(packageName, FileMode.Create))
        //        {
        //            field.SetValue(package, field.GetValue(dummPackage));

        //            foreach (var item in Directory.GetFiles(directory, "*", SearchOption.AllDirectories))
        //            {
        //                var partUri = CreatePartUri(directory, item);
        //                var part = package.CreatePart(partUri, string.Empty, CompressionOption.Maximum);

        //                using (var stream = new FileStream(item, FileMode.Open, FileAccess.Read))
        //                {
        //                    using (var destStream = part.GetStream())
        //                    {
        //                        CopyStream(stream, destStream);
        //                    }
        //                }
        //            }
        //        }
        //    }
        //}

        //private static Uri CreatePartUri(string directory, string item)
        //{
        //    var fileName = item.Replace(directory, string.Empty);
        //    var partUri = PackUriHelper.CreatePartUri(new Uri(fileName, UriKind.Relative));

        //    var field = partUri.GetType().GetField("_partUriString", BindingFlags.Instance | BindingFlags.NonPublic);

        //    field.SetValue(partUri, fileName.Replace("\\", "/"));

        //    return partUri;
        //}

        //private static void CopyStream(Stream source, Stream target)
        //{
        //    const int bufferSize = 0x1000;

        //    var buffer = new byte[bufferSize];
        //    var bytesToRead = 0;

        //    while ((bytesToRead = source.Read(buffer, 0, bufferSize)) > 0)
        //    {
        //        target.Write(buffer, 0, bytesToRead);
        //    }
        //} 

        #endregion
    }
}
